﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SharePoint;

namespace CodeArt.SharePoint.CamlQuery
{
    /// <summary>
    /// the join type
    /// </summary>
    public enum JoinType
    {
        /// <summary>
        /// inner join
        /// </summary>
        INNER,
        /// <summary>
        /// left join
        /// </summary>
        LEFT
    }

    /// <summary>
    /// Join
    /// </summary>
    public class Join : ReturnableQuerySentence, IWhereable, IRelatedJoinable
    {
        internal Join(QueryContext context, JoinType joinType, string field)
            : base(context)
        {
            this.Field = field;
            this.JoinType = joinType;
            this.ForeignListAlias = field;
            context.Joins.Add(this);
        }

        internal Join(QueryContext context, JoinType joinType, string primaryListAlias, string field)
            : base(context)
        {
            this.PrimaryListAlias = primaryListAlias;
            this.Field = field;
            this.JoinType = joinType;
            this.ForeignListAlias = field;
            context.Joins.Add(this);
        }

        internal JoinType JoinType;
        //internal string ListAlias;
        internal string Field;
        internal string PrimaryListAlias;
        internal string ForeignListAlias;

        internal readonly Dictionary<string, string> ProjectedFields = new Dictionary<string, string>();

        public Join As(string listAlias)
        {
            this.ForeignListAlias = listAlias;
            return this;
        }

        public Join Project(string field, string projectFieldName)
        {
            this.ProjectedFields[field] = projectFieldName;
            return this;
        }
        public Join Project(string[] fields, string[] projectFieldNames)
        {
            if (fields.Length != projectFieldNames.Length)
                throw new Exception("length must match.");
            for (var i = 0; i < fields.Length; i++)
            {
                this.ProjectedFields[fields[i]] = projectFieldNames[i];
            }
            return this;
        }

        public Join Project(string field)
        {
            this.ProjectedFields[field] = field;
            return this;
        }

        const string RootJoinXml = @"<Join Type='{0}' ListAlias='{1}'>
    <Eq>
      <FieldRef Name='{2}' RefType='Id'/>
      <FieldRef List='{1}' Name='ID'/>
    </Eq>
  </Join>";
        const string ChildJoinXml = @"<Join Type='{0}' ListAlias='{1}'>
    <Eq>
      <FieldRef List='{3}' Name='{2}' RefType='Id'/>
      <FieldRef List='{1}' Name='ID'/>
    </Eq>
  </Join>";
        internal string GetJoinXml()
        {
            if (String.IsNullOrEmpty(this.PrimaryListAlias))
            {
                return String.Format(RootJoinXml, this.JoinType, this.ForeignListAlias, this.Field);
            }
            else
            {
                return String.Format(ChildJoinXml, this.JoinType, this.ForeignListAlias, this.Field, this.PrimaryListAlias);
            }
        }
        const string FieldProjectedXml = "<Field Name='{0}' Type='Lookup' List='{1}' ShowField='{2}'/>";
        internal string GetProjectedFieldsXml()
        {
            StringBuilder sb = new StringBuilder();
            foreach (var kv in this.ProjectedFields)
            {
                sb.AppendFormat(FieldProjectedXml, kv.Value, this.ForeignListAlias, kv.Key);
                sb.AppendLine();
            }
            return sb.ToString();
        }

        internal static void ApplyJoin(SPQuery query, List<Join> joins)
        {
            if (joins.Count == 0)
                return;

            var xmlJoins = "";
            var xmlProject = "";
            foreach (var join in joins)
            {
                xmlJoins += join.GetJoinXml();
                xmlProject += join.GetProjectedFieldsXml();
            }
            query.Joins = xmlJoins;
            query.ProjectedFields = xmlProject;
        }
    }
}
